package com.xbongbong.paas.script.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.PaasProcessDataEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.model.PaasProcessDataModel;
import com.xbongbong.paas.pojo.dto.FixDataJobDTO;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.config.ProBaseConfig;
import com.xbongbong.pro.constant.XbbProConstant;
import com.xbongbong.pro.domain.entity.UpdateDataEntity;
import com.xbongbong.pro.enums.DistributorMarkEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.InstockProductEntity;
import com.xbongbong.saas.domain.entity.ext.InstockEntityExt;
import com.xbongbong.saas.enums.InstockTypeEnum;
import com.xbongbong.saas.enums.SelectProductEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.InstockEnum;
import com.xbongbong.saas.enums.business.RefundInstockEnum;
import com.xbongbong.saas.model.InstockModel;
import com.xbongbong.saas.model.InstockProductModel;
import com.xbongbong.saas.script.help.InitExplainsHelper;
import com.xbongbong.saas.script.tempenum.TempRefundInstockEnum;
import com.xbongbong.sys.domain.entity.CompanyEntity;
import com.xbongbong.sys.model.CompanyModel;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.MediaType;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;

/**
 * @author xinpeng.jiang
 * @version V1.0
 * @date 2020/12/4 10:39
 * @since V1.0
 */
@RestController
@RequestMapping(XbbProConstant.API_ROOT_PREFIX + "/script/refund/instock")
public class RefundInstockController {
    private static final Logger LOG = LoggerFactory.getLogger(RefundInstockController.class);
    @Resource
    private ProBaseConfig proBaseConfig;
    @Resource
    private CompanyModel companyModel;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private InstockProductModel instockProductModel;
    @Resource
    private InstockModel instockModel;
    @Resource
    private EsHelper esHelper;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private PaasProcessDataModel paasProcessDataModel;

    @RequestMapping(value = "/addExplainOfMoney", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public void addExplainOfMoney(@RequestBody @Valid FixDataJobDTO fixDataJobDTO, BindingResult br) throws Exception {
        if (!proBaseConfig.getXbbCode().equals(fixDataJobDTO.getXbbCode())) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006, SystemErrorCodeEnum.API_ERROR_100006.getMsg());
        }
        LocaleContextHolder.setLocale(Locale.CHINA);
        long start = DateTimeUtil.getInt();
        List<String> corpidList = fixDataJobDTO.getCorpidList();
        if (CollectionsUtil.isEmpty(corpidList)) {
            corpidList = companyModel.getCorpidList();
            corpidList.add("0");
        }
        LOG.warn("数据库中共有公司{}家", corpidList.size());
        int corpidCount = 0;
        List<FieldAttrEntity> fieldAttrList = new ArrayList<>();
        List<String> enumAttrs = InitExplainsHelper.getBusinessEnumAttrs(RefundInstockEnum.class);
        List<?> newFieldEnumList = Arrays.asList(RefundInstockEnum.PRODUCT_TOTAL, RefundInstockEnum.OTHER_EXPENSE, RefundInstockEnum.TOTAL_MONEY);

        for (Object newFiledEnum : newFieldEnumList) {
            FieldAttrEntity newFieldAttrEntity = InitExplainsHelper.setField(RefundInstockEnum.class, enumAttrs, newFiledEnum);
            try {
                Method method = TempRefundInstockEnum.class.getMethod("init" + newFieldAttrEntity.getSaasAttr().substring(0, 1).toUpperCase() + newFieldAttrEntity.getSaasAttr().substring(1), FieldAttrEntity.class);
                method.invoke(TempRefundInstockEnum.class, newFieldAttrEntity);
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
            newFieldAttrEntity.setIsRedundant(0);
            fieldAttrList.add(newFieldAttrEntity);
        }

        HashSet<String> attrList = new HashSet<>();
        for (FieldAttrEntity fieldAttrEntity : fieldAttrList) {
            attrList.add(fieldAttrEntity.getAttr());
        }
        int businessType = XbbRefTypeEnum.REFUND_INSTOCK.getCode();
        Map<String, Object> param = new HashMap<>(4);
        param.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
        param.put(StringConstant.BUSINESS_TYPE, businessType);
        param.put(StringConstant.DISTRIBUTOR_MARK, DistributorMarkEnum.OTHER.getCode());
        for (String corpid : corpidList) {
            param.put(ParameterConstant.CORPID, corpid);
            List<PaasFormExplainEntity> entitys = paasFormExplainModel.getNoHandleExplainsByFindEntitys(param);
            for (PaasFormExplainEntity entity : entitys) {
                if (Objects.equals("[]", entity.getExplains())) {
                    continue;
                }
                boolean canInsert = true;
                List<FieldAttrEntity> fieldAttrEntityList = JSONArray.parseArray(entity.getExplains(), FieldAttrEntity.class);

                int index = 0;
                for (int i = 0; i < fieldAttrEntityList.size(); i++) {
                    FieldAttrEntity fieldAttrEntity = fieldAttrEntityList.get(i);
                    //插入到当前字段后面
                    if (Objects.equals(fieldAttrEntity.getAttr(), RefundInstockEnum.PRODUCT.getAttr())) {
                        index = i + 1;
                    }
                    if (attrList.contains(fieldAttrEntity.getAttr())) {
                        canInsert = false;
                        LOG.warn("{}公司已经存在{}字段", corpid, fieldAttrEntity.getAttr());
                        break;
                    }
                }
                if (canInsert && index != 0) {
                    fieldAttrEntityList.addAll(index, fieldAttrList);
                    String explains = JSONObject.toJSONString(fieldAttrEntityList);
                    entity.setExplains(explains);
                    entity.setUpdateTime(DateTimeUtil.getInt());
                    paasFormExplainModel.update(entity);
                }
            }
            // 删redis中缓存
            String key = corpid + StringConstant.CROSS + businessType;
            paasRedisHelper.removeValue(RedisPrefixConstant.FIELD_EXPLANATION, key);

            corpidCount++;
            LOG.warn("公司{}:{}/{}", corpid, corpidCount, corpidList.size());
        }

        LOG.warn("consume:{}", (DateTimeUtil.getInt() - start));
        LOG.warn("操作成功");
    }

    @RequestMapping(value = "/addDataOfMoney", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public void addDataOfMoney(@RequestBody @Valid FixDataJobDTO fixDataJobDTO, BindingResult br) throws Exception {
        if (!proBaseConfig.getXbbCode().equals(fixDataJobDTO.getXbbCode())) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006, SystemErrorCodeEnum.API_ERROR_100006.getMsg());
        }
        LocaleContextHolder.setLocale(Locale.CHINA);
        long start = DateTimeUtil.getInt();
        List<String> corpidList = fixDataJobDTO.getCorpidList();
        if (CollectionsUtil.isEmpty(corpidList)) {
            corpidList = companyModel.getCorpidList();
        }
        LOG.warn("数据库中共有公司{}家", corpidList.size());

        int refreshCorp = 0;
        int noRefreshCorp = 0;
        String keyword = StringConstant.POINT + StringConstant.KEY_WORD;
        for (String corpid : corpidList) {
            // 获取formId，为了和经销商区分
            int businessType = XbbRefTypeEnum.REFUND_INSTOCK.getCode();
            Map<String, Object> param = new HashMap<>(4);
            param.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
            param.put(StringConstant.BUSINESS_TYPE, businessType);
            param.put(ParameterConstant.CORPID, corpid);
            param.put(StringConstant.DISTRIBUTOR_MARK, DistributorMarkEnum.OTHER.getCode());
            List<PaasFormEntityExt> paasFormEntityExtList = paasFormModel.findEntitys(param);
            // 没formId直接跳过
            if (CollectionsUtil.isEmpty(paasFormEntityExtList)) {
                noRefreshCorp++;
                continue;
            }
            // 拼接ES查询语句
            BoolQueryBuilder boolQueryBuilder = boolQuery();
            boolQueryBuilder.filter(termQuery(ParameterConstant.DEL, DelEnum.NORMAL.getDel()));
            boolQueryBuilder.filter(termQuery(InstockEnum.getEsAttr4Keyword(InstockEnum.TYPE), InstockTypeEnum.RED_CONTRACT_INSTOCK.getCode()));
            boolQueryBuilder.mustNot(existsQuery(RefundInstockEnum.getEsAttr4Keyword(RefundInstockEnum.PRODUCT_TOTAL)));
            boolQueryBuilder.filter(termQuery(ParameterConstant.CORPID + keyword, corpid));
            boolQueryBuilder.filter(termQuery(ParameterConstant.FORMID, paasFormEntityExtList.get(0).getId()));
            List<String> fieldList = new ArrayList<>(1);
            fieldList.add(StringConstant.DATA_ID);
            List<InstockEntityExt> instockEntityExtList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_INSTOCK, boolQueryBuilder, InstockEntityExt.class, fieldList);
            // 提取instockId
            List<Long> instockIdList = new ArrayList<>(instockEntityExtList.size());
            instockEntityExtList.forEach(i -> instockIdList.add(i.getDataId()));
            // 手动分页
            int pageSize = 1000;
            for (int i = 0, listSize = instockIdList.size(); i < listSize; i++) {
                int toIndex = i + pageSize;
                if (toIndex > listSize) {
                    toIndex = listSize;
                }
                List<Long> instockIdListByPage = instockIdList.subList(i, toIndex);
                i = toIndex;

                // 拿所有产品
                List<InstockProductEntity> instockProductEntityList = instockProductModel.getProductsByInstockIdIn(corpid, instockIdListByPage);
                // 分组
                Map<Long, List<InstockProductEntity>> instockProductEntityMap = instockProductEntityList.stream().collect(Collectors.groupingBy(InstockProductEntity::getIntoWarehouseId));
                // 求和
                HashMap<Long, Double> moneyMap = new HashMap<>(instockProductEntityMap.size());
                instockProductEntityMap.forEach((k, v) -> {
                    double allPrice = 0D;
                    for (InstockProductEntity j : v) {
                        double subTotal = Arith.mul(j.getProductNum() == null ? 0D : j.getProductNum(), j.getPrice() == null ? 0D : j.getPrice());
                        allPrice = Arith.add(allPrice, subTotal);
                    }
                    moneyMap.put(k, allPrice);
                });
                // 刷数据
                ArrayList<UpdateDataEntity> updateDataList = new ArrayList<>(instockIdListByPage.size());
                for (Long instockId : instockIdListByPage) {
                    JSONObject data = new JSONObject();
                    // 没有产品刷0
                    if (!moneyMap.containsKey(instockId)) {
                        data.put(RefundInstockEnum.PRODUCT_TOTAL.getAttr(), 0);
                        data.put(RefundInstockEnum.OTHER_EXPENSE.getAttr(), 0);
                        data.put(RefundInstockEnum.TOTAL_MONEY.getAttr(), 0);
                    } else {
                        data.put(RefundInstockEnum.PRODUCT_TOTAL.getAttr(), moneyMap.get(instockId));
                        data.put(RefundInstockEnum.OTHER_EXPENSE.getAttr(), 0);
                        data.put(RefundInstockEnum.TOTAL_MONEY.getAttr(), moneyMap.get(instockId));
                    }

                    UpdateDataEntity updateDataEntity = ExplainUtil.getUpdateData(instockId, data, corpid);
                    updateDataList.add(updateDataEntity);
                }
                // 更新
                if (CollectionsUtil.isNotEmpty(updateDataList)) {
                    instockModel.updateBatch(updateDataList, corpid, WriteRequest.RefreshPolicy.NONE);
                }
            }
            refreshCorp++;
            LOG.warn("公司{}:{}/{}", corpid, refreshCorp, corpidList.size());
        }

        LOG.warn("无需刷的公司:{}家", noRefreshCorp);
        LOG.warn("consume:{}s", (DateTimeUtil.getInt() - start));
        LOG.warn("操作成功");
    }

    @RequestMapping(value = "/addDataForProcess", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public void addDataForProcess(@RequestBody @Valid FixDataJobDTO fixDataJobDTO, BindingResult br) throws Exception {
        if (!proBaseConfig.getXbbCode().equals(fixDataJobDTO.getXbbCode())) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006, SystemErrorCodeEnum.API_ERROR_100006.getMsg());
        }
        LocaleContextHolder.setLocale(Locale.CHINA);
        long start = DateTimeUtil.getInt();
        List<String> corpidList = fixDataJobDTO.getCorpidList();
        if (CollectionsUtil.isEmpty(corpidList)) {
            Map<String, Object> param = new HashMap<>();
            param.put(ParameterConstant.COLUMNS, ParameterConstant.CORPID);
            param.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
            param.put("status", BasicConstant.ONE);
            param.put("feeEndTimeGte", DateTimeUtil.getInt());
            List<CompanyEntity> companyEntityList = companyModel.findEntitys(param);
            companyEntityList.forEach(i -> corpidList.add(i.getCorpid()));
        }
        LOG.warn("数据库中共有公司{}家", corpidList.size());

        for (int i = 0; i < corpidList.size(); i++) {
            String corpid = corpidList.get(i);
            // 获取formId，排除经销商
            int businessType = XbbRefTypeEnum.REFUND_INSTOCK.getCode();
            Map<String, Object> param = new HashMap<>();
            param.put(ParameterConstant.COLUMNS, ParameterConstant.ID);
            param.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
            param.put(StringConstant.BUSINESS_TYPE, businessType);
            param.put(ParameterConstant.CORPID, corpid);
            param.put(StringConstant.DISTRIBUTOR_MARK, DistributorMarkEnum.OTHER.getCode());
            List<PaasFormEntityExt> paasFormEntityExtList = paasFormModel.findEntitys(param);
            // 没formId直接跳过
            if (CollectionsUtil.isEmpty(paasFormEntityExtList)) {
                LOG.warn("公司{}:{}/{}", corpid, i + 1, corpidList.size());
                continue;
            }
            // 手动分页
            Map<String, Object> dataParam = new HashMap<>();
            dataParam.put(ParameterConstant.CORPID, corpid);
            dataParam.put(ParameterConstant.FORMID, paasFormEntityExtList.get(0).getId());
            dataParam.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
            Integer dataCount = paasProcessDataModel.getEntitysCount(dataParam);
            for (int pageNum = 0, pageSize = 1000; pageNum * pageSize < dataCount; pageNum++) {
                int startPage = pageNum * pageSize;
                dataParam.put(ParameterConstant.START, startPage);
                dataParam.put(ParameterConstant.PAGE_NUM, pageSize);
                // 获取流程数据
                List<PaasProcessDataEntity> processDataEntityList = paasProcessDataModel.list(dataParam);
                if (CollectionsUtil.isEmpty(processDataEntityList)) {
                    break;
                }
                boolean needUpdate = false;
                for (PaasProcessDataEntity paasProcessDataEntity : processDataEntityList) {
                    JSONObject data = JSONObject.parseObject(paasProcessDataEntity.getData());
                    if (Objects.nonNull(data)) {
                        Double productTotal = data.getDouble(RefundInstockEnum.PRODUCT_TOTAL.getAttr());
                        Double otherExpense = data.getDouble(RefundInstockEnum.OTHER_EXPENSE.getAttr());
                        Double totalMoney = data.getDouble(RefundInstockEnum.TOTAL_MONEY.getAttr());
                        // 只刷为空的字段
                        if (Objects.isNull(productTotal)) {
                            needUpdate = true;
                            productTotal = 0D;
                            JSONArray productArr = data.getJSONArray(RefundInstockEnum.PRODUCT.getAttr());
                            if (CollectionsUtil.isNotEmpty(productArr)) {
                                for (Object product : productArr) {
                                    JSONObject productObj = (JSONObject) product;
                                    if (Objects.nonNull(productObj.getDouble(SelectProductEnum.SUBTOTAL.getAttr()))) {
                                        productTotal += productObj.getDouble(SelectProductEnum.SUBTOTAL.getAttr());
                                    }
                                }
                            }
                            data.put(RefundInstockEnum.PRODUCT_TOTAL.getAttr(), productTotal);
                        }
                        if (Objects.isNull(otherExpense)) {
                            needUpdate = true;
                            otherExpense = 0D;
                            data.put(RefundInstockEnum.OTHER_EXPENSE.getAttr(), otherExpense);
                        }
                        if (Objects.isNull(totalMoney)) {
                            needUpdate = true;
                            totalMoney = Arith.add(productTotal, otherExpense);
                            data.put(RefundInstockEnum.TOTAL_MONEY.getAttr(), totalMoney);
                        }
                        if (needUpdate) {
                            paasProcessDataEntity.setData(data.toJSONString());
                        }
                    }
                }
                // 刷回去
                if (needUpdate) {
                    paasProcessDataModel.updateBatch(processDataEntityList, corpid);
                }
            }
            LOG.warn("公司{}:{}/{}", corpid, i + 1, corpidList.size());
        }
        LOG.warn("consume:{}s", (DateTimeUtil.getInt() - start));
        LOG.warn("操作成功");
    }

}
